commonlibsse_ng\skse\interfaces/
load.rs

1// C++ Original code
2// - ref: https://github.com/SARDONYX-forks/CommonLibVR/blob/ng/include/SKSE/Interfaces.h
3// - ref: https://github.com/SARDONYX-forks/CommonLibVR/blob/ng/src/SKSE/Interfaces.cpp
4// SPDX-FileCopyrightText: (C) 2018 Ryan-rsm-McKenzie
5// SPDX-License-Identifier: MIT
6//
7// SPDX-FileCopyrightText: (C) 2025 SARDONYX
8// SPDX-License-Identifier: Apache-2.0 OR MIT
9
10use crate::rel::version::Version;
11use crate::skse::impls::stab::{
12    PluginHandle, PluginInfo, SKSEInterface, SKSEMessagingInterface, SKSEObjectInterface,
13    SKSEPapyrusInterface, SKSEScaleformInterface, SKSESerializationInterface, SKSETaskInterface,
14    SKSETrampolineInterface,
15};
16use crate::skse::interfaces::query::QueryInterface;
17use core::ffi::{CStr, c_void};
18
19/// Aimed at providing an API.
20///
21/// That is equivalent in memory layout to `SKSEInterface` and easy to use,
22/// in order to allow it to be used instead of the argument `SKSEInterface` in the `SKSEPlugin_Load`
23/// symbol.
24#[derive(Debug)]
25#[repr(transparent)]
26pub struct LoadInterface(SKSEInterface);
27
28/// Type conversion trait implemented for types searchable by `query_interface`.
29///
30/// This trait is sealed and cannot be implemented for types outside of `commonlibsse_ng`.
31///
32/// # Safety
33/// This can only be implemented by interfaces defined in SKSE.
34pub unsafe trait QueryTarget: private::Sealed {
35    /// Cast to function table(struct)
36    ///
37    /// # Panics(Dev)
38    /// ptr is null.
39    #[inline]
40    fn cast(ptr: *mut c_void) -> &'static Self
41    where
42        Self: Sized,
43    {
44        debug_assert!(!ptr.is_null(), "SKSE interface query returned null");
45        unsafe { &*(ptr as *const Self) }
46    }
47
48    /// Query ID
49    const ID: u32;
50}
51
52macro_rules! impl_query_target {
53    ($($t:ty => $id:expr),*) => {
54        $(
55            unsafe impl QueryTarget for $t {
56                const ID: u32 = $id;
57            }
58        )*
59    };
60}
61
62// Prevent users from implementing the `QueryTarget` trait.
63mod private {
64    use super::*;
65
66    pub trait Sealed {}
67    impl Sealed for SKSEScaleformInterface {}
68    impl Sealed for SKSEPapyrusInterface {}
69    impl Sealed for SKSESerializationInterface {}
70    impl Sealed for SKSETaskInterface {}
71    impl Sealed for SKSEMessagingInterface {}
72    impl Sealed for SKSEObjectInterface {}
73    impl Sealed for SKSETrampolineInterface {}
74}
75
76impl_query_target!(
77    SKSEScaleformInterface => 1,
78    SKSEPapyrusInterface => 2,
79    SKSESerializationInterface => 3,
80    SKSETaskInterface => 4,
81    SKSEMessagingInterface => 5,
82    SKSEObjectInterface => 6,
83    SKSETrampolineInterface => 7
84);
85
86impl LoadInterface {
87    /// Get the plugin handle (index of how many dlls SKSE has loaded) of this SKSE plugin dll.
88    #[inline]
89    pub fn get_plugin_handle(&self) -> PluginHandle {
90        unsafe { (self.0.GetPluginHandle)() }
91    }
92
93    /// Get information about a plugin given its name.
94    ///
95    /// Returns a pointer to `PluginInfo` if found, otherwise `null`.
96    #[inline]
97    pub fn get_plugin_info(&self, name: &CStr) -> *const PluginInfo {
98        unsafe { (self.0.GetPluginInfo)(name.as_ptr()) }
99    }
100
101    /// Get the release index of the plugin system.
102    #[inline]
103    pub fn get_release_index(&self) -> u32 {
104        unsafe { (self.0.GetReleaseIndex)() }
105    }
106
107    /// Get a reference to the global variables for each interface.
108    #[inline]
109    pub fn query_interface<T: QueryTarget>(&self) -> &'static T {
110        let fn_table = unsafe { (self.0.QueryInterface)(T::ID) };
111        T::cast(fn_table)
112    }
113}
114
115impl QueryInterface for LoadInterface {
116    #[inline]
117    fn editor_version(&self) -> u32 {
118        self.0.editorVersion
119    }
120
121    #[inline]
122    fn is_editor(&self) -> bool {
123        self.0.isEditor != 0
124    }
125
126    #[inline]
127    fn runtime_version(&self) -> Version {
128        let packed = self.0.runtimeVersion;
129        let major = ((packed & 0xFF000000) >> 24) as u16;
130        let minor = ((packed & 0x00FF0000) >> 16) as u16;
131        let revision = ((packed & 0x0000FFF0) >> 4) as u16;
132        let build = (packed & 0x0000000F) as u16;
133        Version::new(major, minor, revision, build)
134    }
135
136    #[inline]
137    fn skse_version(&self) -> u32 {
138        self.0.skseVersion
139    }
140}